`timescale 1ns / 1ps

module pwc_reorder
#(
    parameter N_HI      = 4,
    parameter N_IO      = 2,
    parameter N_BK      = 16,
    parameter N_CH      = 512,
    parameter BIT       = 16,
    parameter RAM_TYPE  = "block",
    parameter RAM_LATENCY = 2
)
(
    input   clk,
    input   rst,
    
    input   i_vld,
    output  i_rdy,
    input   [N_IO*BIT-1 : 0]    i_data,
    
    output  o_vld,
    input   o_rdy,
    output  [N_IO*BIT-1 : 0]    o_data
);

localparam DEPTH = N_CH / N_IO * 2;
localparam WIDTH = N_IO * BIT;

localparam FOLD_I = N_BK / N_IO;
localparam FOLD_O = N_CH / N_IO;

wire              fi_vld  [N_HI-1: 0];
wire              fi_rdy  [N_HI-1: 0];
wire              fo_vld  [N_HI-1: 0];
wire              fo_rdy  [N_HI-1: 0];
wire [WIDTH-1: 0] fo_data [N_HI-1: 0];

wire [$clog2(N_HI)-1: 0] i_sel;
wire [$clog2(N_HI)-1: 0] o_sel;

wire i_ena;
wire i_last;
wire o_ena;
wire o_last;

assign i_rdy  = fi_rdy [i_sel];
assign o_vld  = fo_vld [o_sel];
assign o_data = fo_data[o_sel];

assign i_ena = i_rdy & i_vld;
assign o_ena = o_rdy & o_vld;

zq_counter #(
    .N  (N_HI)
) inst_cnt_si
(
    .clk    (clk),
    .rst    (rst),
    .clken  (i_ena & i_last),
    .last   (),
    .out    (i_sel)
);
zq_counter #(
    .N  (N_HI)
) inst_cnt_so
(
    .clk    (clk),
    .rst    (rst),
    .clken  (o_ena & o_last),
    .last   (),
    .out    (o_sel)
);

zq_counter #(
    .N  (FOLD_I)
) inst_cnt_fi
(
    .clk    (clk),
    .rst    (rst),
    .clken  (i_ena),
    .last   (i_last),
    .out    ()
);
zq_counter #(
    .N  (FOLD_O)
) inst_cnt_fo
(
    .clk    (clk),
    .rst    (rst),
    .clken  (o_ena),
    .last   (o_last),
    .out    ()
);

genvar i;
generate
    for (i = 0; i < N_HI; i = i + 1)
    begin
        assign fi_vld[i] = (i_sel == i) ? i_vld : 1'b0;
        assign fo_rdy[i] = (o_sel == i) ? o_rdy : 1'b0;

        zq_fifo #(
            .WIDTH      ( WIDTH       ),
            .DEPTH      ( DEPTH       ),
            .RAMTYPE    ( RAM_TYPE    ),
            .RAMLATENCY ( RAM_LATENCY )
        )
        inst_fifo (
            .clk                     ( clk       ),
            .rst                     ( rst       ),

            .in_vld                  ( fi_vld[i]  ),
            .in_rdy                  ( fi_rdy[i]  ),
            .din                     (  i_data    ),

            .out_vld                 ( fo_vld[i]  ),
            .out_rdy                 ( fo_rdy[i]  ),
            .dout                    ( fo_data[i] )
        );
    end
endgenerate

endmodule
